﻿/////////////////////////////////////////////////////////////////////////////////
//
// InstantCube
// - Automatically generates Analysis Services cubes
// (C) Copyright Johan Åhlén 2009
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//
// More info on InstantCube website: http://instantcube.codeplex.com
//
/////////////////////////////////////////////////////////////////////////////////


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using AdvantIQ.InstantCube.Model;
using AdvantIQ.InstantCube.Emitters;

namespace AdvantIQ.InstantCube.UI
{
    internal delegate void SetStatusBarTextDelegate(string text);
    internal delegate void BackgroundTaskDelegate(Project project);
    internal delegate void AddToListViewDelegate(string line, string col, string length, string message);

    public partial class MainWnd : Form
    {
        private string FileName = "";
        private Parser Parser = new Parser();

        public MainWnd()
        {
            InitializeComponent();
        }

        private DialogResult checkSave()
        {
            if (!textBox1.Modified)
                return DialogResult.OK;

            return MessageBox.Show(this, "You have unsaved changes that you will loose. Do you want to proceed?",
                "Unsaved changes", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
        }

        private DialogResult checkExit()
        {
            if (!textBox1.Modified)
                return DialogResult.No;

            return MessageBox.Show(this, "You have unsaved changes. Do you wish to save?",
                "Exit", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
        }

        private void SetStatusBarText(string text)
        {
            if (InvokeRequired)
                this.Invoke(new SetStatusBarTextDelegate(this.SetStatusBarText), text);
            else
                toolStripStatusLabel1.Text = text;
        }

        private Project Parse()
        {
            try
            {
                var root = Parser.Parse(textBox1.Text);
                var project = new Project(root);

                ClearListView();
                SetStatusBarText("Parse successful!");
                return project;
            }
            catch (ModelErrorException ex)
            {
                ClearListView();
                AddToListView(
                    "",
                    "",
                    "",
                    ex.GetDescription()
                    );

                SetStatusBarText("Model error!");
                return null;
            }
            catch (Exception ex)
            {
                ClearListView();
                AddToListView(
                    "",
                    "",
                    "",
                    ex.Message + " " + ex.StackTrace
                    );

                SetStatusBarText("General error!");
                return null;
            }
        }

        private void emptyProjectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (checkSave() != DialogResult.OK)
                return;

            textBox1.Clear();
            FileName = "";
            textBox1.Modified = false;
        }

        private void fromSampleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (checkSave() != DialogResult.OK)
                return;

            textBox1.Text = ResourceReader.ReadResourceStr("Sample.dwml");
            FileName = "";
            textBox1.Modified = false;
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (checkSave() != DialogResult.OK)
                return;

            var dlg = new OpenFileDialog() 
                { Filter = "Instant Cube Files (*.ic)|*.ic|All Files (*.*)|*.*" };
            if (dlg.ShowDialog(this) != DialogResult.OK)
                return;

            var sr = new StreamReader(dlg.FileName);
            textBox1.Text = sr.ReadToEnd();
            sr.Close();
            FileName = dlg.FileName;
            textBox1.Modified = false;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(FileName))
            {
                saveAsToolStripMenuItem_Click(sender, e);
                return;
            }

            var sw = new StreamWriter(FileName, false);
            sw.Write(textBox1.Text);
            sw.Close();
            textBox1.Modified = false;
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dlg = new SaveFileDialog() 
            { FileName = this.FileName, Filter = "Instant Cube Files (*.ic)|*.ic|All Files (*.*)|*.*" };
            if (dlg.ShowDialog(this) != DialogResult.OK)
                return;

            FileName = dlg.FileName;
            var sw = new StreamWriter(FileName, false);
            sw.Write(textBox1.Text);
            sw.Close();
            textBox1.Modified = false;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void parseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Parse();
        }

        private void emitSQLDatabase(Project project)
        {
            var sqlEmitter = new Emitters.SQLEmitter(project);

            SetStatusBarText("Creating Database");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitCreateDatabase());
            SetStatusBarText("Creating Fact Tables");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitFactTables());
            SetStatusBarText("Creating Dimension Tables");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitDimensionTables());
            SetStatusBarText("Creating Foreign Keys");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitForeignKeys());
            SetStatusBarText("Inserting Dimension Data");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitDimensionData());
            SetStatusBarText("Inserting Sample Data");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitFactSampleData());
            if (Properties.Settings.Default.AutoGrantSSASUser)
            {
                SetStatusBarText("Setting security");
                sqlEmitter.ExecuteSQL(sqlEmitter.EmitSecurity(Properties.Settings.Default.SSASUser));
            }
            SetStatusBarText("Emit SQL successful!");
        }

        private void emitSSAS(Project project)
        {
            var ssasEmitter = new Emitters.SSASEmitter(project);

            SetStatusBarText("Creating Analysis Services Database");
            ssasEmitter.EmitDatabase();
            SetStatusBarText("Creating Data Source");
            ssasEmitter.EmitDataSource();
            SetStatusBarText("Creating Data Source View");
            ssasEmitter.EmitDSV();
            SetStatusBarText("Creating Dimensions");
            ssasEmitter.EmitDimensions();
            SetStatusBarText("Creating Cubes");
            ssasEmitter.EmitCubes();
            SetStatusBarText("Emit SSAS successful!");
        }

        private void dropSQL(Project project)
        {
            var sqlEmitter = new Emitters.SQLEmitter(project);

            SetStatusBarText("Dropping Database");
            sqlEmitter.ExecuteSQL(sqlEmitter.EmitDropDatabase());
            SetStatusBarText("Drop SQL Database successful!");
        }

        private void deploySQLToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void deploySSASToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var project = Parse();
            if (project == null)
                return;

            if (backgroundWorker1.IsBusy)
                return;

            backgroundWorker1.RunWorkerAsync(new object[] 
                { new BackgroundTaskDelegate(emitSSAS), project });
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dlg = new About();
            dlg.ShowDialog(this);
        }

        private void MainWnd_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason != CloseReason.UserClosing)
                return;

            switch (checkExit())
            {
                case DialogResult.Yes:
                    saveAsToolStripMenuItem_Click(sender, e);
                    return;
                case DialogResult.No:
                    return;
                case DialogResult.Cancel:
                    e.Cancel = true;
                    return;
            }
        }

        private void textBox1_SelectionChanged(object sender, EventArgs e)
        {
            var line = textBox1.GetLineFromCharIndex(textBox1.SelectionStart);
            var column = textBox1.SelectionStart - textBox1.GetFirstCharIndexFromLine(line);
            var lastline = textBox1.GetLineFromCharIndex(textBox1.TextLength);

            toolStripStatusLabel2.Text = String.Format("Line {0} Column {1}",
                line, column);
        }

        private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            try
            {
                var item = listView1.SelectedItems[0];
                var line = int.Parse(item.SubItems[0].Text);
                var col = int.Parse(item.SubItems[1].Text);
                var length = int.Parse(item.SubItems[2].Text);

                textBox1.Select(textBox1.GetFirstCharIndexFromLine(line - 1) + col-1, length);
                textBox1.ScrollToCaret();
            }
            catch
            {
            }
        }

        private void listView1_SizeChanged(object sender, EventArgs e)
        {
            columnHeader4.Width = listView1.ClientSize.Width - columnHeader1.Width -
                columnHeader2.Width - columnHeader3.Width;
        }

        private void MainWnd_Load(object sender, EventArgs e)
        {
            listView1_SizeChanged(sender, e);
            textBox1_SelectionChanged(sender, e);
        }

        private void cancelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
            DisableCancel();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            var arr = (object[])e.Argument;
            var func = (BackgroundTaskDelegate)arr[0];
            var project = (Project)arr[1];

            try
            {
                EnableCancel();
                func.Invoke(project);
                ClearListView();
            }
            catch (Exception ex)
            {
                ClearListView();
                AddToListView(
                    "",
                    "",
                    "",
                    ex.Message);

                //SetStatusBarText("Error!");
            }
            finally
            {
                DisableCancel();
            }
        }

        private void ClearListView()
        {
            if (this.InvokeRequired)
                this.Invoke(new MethodInvoker(ClearListView));
            else
                listView1.Items.Clear();
        }

        private void AddToListView(string line, string col, string length, string message)
        {
            if (this.InvokeRequired)
                this.Invoke(new AddToListViewDelegate(AddToListView), line, col, length, message);
            else
            {
                var item = new ListViewItem(new string[] { line, col, length, message });
                listView1.Items.Add(item);
            }
        }

        private void EnableCancel()
        {
            if (this.InvokeRequired)
                this.Invoke(new MethodInvoker(EnableCancel));
            else
                cancelToolStripMenuItem.Enabled = true;
        }

        private void DisableCancel()
        {
            if (this.InvokeRequired)
                this.Invoke(new MethodInvoker(DisableCancel));
            else
                cancelToolStripMenuItem.Enabled = false;
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(this, "This will delete all contents of the SQL Database. Are you sure?",
                "Drop SQL Database", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) !=
                DialogResult.OK)
                return;

            var project = Parse();
            if (project == null)
                return;

            if (backgroundWorker1.IsBusy)
                return;

            backgroundWorker1.RunWorkerAsync(new object[] { new BackgroundTaskDelegate(dropSQL), project });
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop(); 

            if (!Properties.Settings.Default.DisableFirstTimeDlg)
            {
                var dlg = new FirstTime();
                dlg.ShowDialog(this);
            }
        }

        private void deploySQLtoQueryWindowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var project = Parse();
            if (project == null)
                return;

            var sqlEmitter = new Emitters.SQLEmitter(project);
            var sb = new StringBuilder();

            SetStatusBarText("Creating Database");
            sb.Append(sqlEmitter.EmitCreateDatabase());
            SetStatusBarText("Creating Fact Tables");
            sb.Append(sqlEmitter.EmitFactTables());
            SetStatusBarText("Creating Dimension Tables");
            sb.Append(sqlEmitter.EmitDimensionTables());
            SetStatusBarText("Creating Foreign Keys");
            sb.Append(sqlEmitter.EmitForeignKeys());
            SetStatusBarText("Inserting Dimension Data");
            sb.Append(sqlEmitter.EmitDimensionData());
            SetStatusBarText("Inserting Sample Data");
            sb.Append(sqlEmitter.EmitFactSampleData());
            if (Properties.Settings.Default.AutoGrantSSASUser)
            {
                SetStatusBarText("Setting security");
                sb.Append(sqlEmitter.EmitSecurity(Properties.Settings.Default.SSASUser));
            }
            SetStatusBarText("Emit SQL successful!");

            var dlg = new TraceWnd();
            dlg.SourceCode = sb.ToString();
            dlg.ShowDialog();
        }

        private void deploySQLtoDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var project = Parse();
            if (project == null)
                return;

            if (backgroundWorker1.IsBusy)
                return;

            backgroundWorker1.RunWorkerAsync(new object[] { new BackgroundTaskDelegate(emitSQLDatabase), project });
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dlg = new SettingsWnd();
            dlg.ShowDialog();
        }
    }
}
